<?php
/**
 * Created by PhpStorm.
 * User: july
 * Date: 2017/6/28
 * Time: 10:14
 */

namespace business\search\models;

use business\base\services\DictionaryService;
use business\base\services\SystemConfigService;
use business\bigdata\model\SupplierModel;
use business\elasticsearch\models\BigDataSupplierModel;
use \business\elasticsearch\models\SupplierWordsModel;
use common\utils\StringUtils;
use search\models\BaseSearchForm;
use yii\elasticsearch\Command;

class SupplierBigdataSearchForm extends BaseSearchForm
{
    /**
     * @var string 供应商id
     */
    public $supplierId;

    /**
     * @var string 公司所在地code
     */
    public $locationCode;

    /**
     * @var array 公司服务一级分类code
     */
    public $businessCode1;

    /**
     * @var array 公司服务二级分类code
     */
    public $businessCode2;

    /**
     * @var array 公司服务三级分类code
     */
    public $businessCode3;

    /**
     * @var array 公司项目案例
     */
    public $supplierProjectCase;

    /**
     * @var string 服务分类3级分类
     */
    public $qualificationCode;

    public $registCapiRmbMin;//注册资本

    public $registCapiRmbMax;

    public $supplierProjectCaseNumberMin;//项目案例

    public $supplierProjectCaseNumberMax;

    public $cooperationDevelopersNumberMin;//合作关系

    public $cooperationDevelopersNumberMax;

    public $isTopDeveloper;//是否有百强合作关系

    // 排序字段，倒序
    public $orderBy;

    public $segmentWords;

    public $segmentFacet;

    public $sort = 'desc';

    public $newWay = false;

    private static $prefix = '<em class="_high">';
    private static $suffix = '</em>';

    public function rules()
    {
        return [
            [array_keys($this->getAttributes()), 'safe'],
        ];
    }

    /**
     * 通用搜索查詢
     */
    public function search()
    {
        if ($this->newWay) {
            $this->segmentFacet = $this->constructWords();
        }
        $data = $this->searchDocuments($this->builderQueryDSL());
        $documents = $data['hits']['hits'];
        $result['error_code'] = 0;
        $result['found_number'] = $data['hits']['total'];
        $result['max_score'] = (int)$data['hits']['max_score'];
        $result['stats'] = [];
        $result['data'] = [];

        $statsResult = $data['aggregations'];
        foreach ($statsResult as $key => $item) {
            list($prefix, $realKey) = explode('_', $key);
            $result['stats'][$realKey][$prefix] = (int)$item['value'];
        }
        if (empty($data['hits']['total'])) {
            return $result;
        }
        $preciseMatchScore = SystemConfigService::get('supplierbigdata_precise_match_score');
        foreach ($documents as $key => $item) {
            $model = new SupplierModel();
            $model->attributes = $item['_source'];
            $highlightedFields = $item['highlight'];
            if (isset($highlightedFields['supplierName']) && is_array($highlightedFields['supplierName'])) {
                $model->supplierName = $highlightedFields['supplierName'][0];
            }
            if (isset($highlightedFields['supplierIntroduce']) && is_array($highlightedFields['supplierIntroduce'])) {
                $startWord = mb_substr($highlightedFields['supplierIntroduce'][0], 0, 4, 'utf-8');
                $endWord = mb_substr($highlightedFields['supplierIntroduce'][0], -5, null, 'utf-8');
                if ($startWord != self::$prefix && !StringUtils::startWith($model->supplierIntroduce, $startWord)) {
                    $tempIntroduce = "..." . $highlightedFields['supplierIntroduce'][0];
                } else {
                    $tempIntroduce = $highlightedFields['supplierIntroduce'][0];
                }
                if ($endWord != self::$suffix && !StringUtils::endWith($model->supplierIntroduce, $endWord)) {
                    $tempIntroduce = $tempIntroduce . '...';
                }
                $model->supplierIntroduce = $tempIntroduce;
            }
            if (isset($highlightedFields['supplierScope']) && is_array($highlightedFields['supplierScope'])) {
                $startWord = mb_substr($highlightedFields['supplierScope'][0], 0, 4, 'utf-8');
                $endWord = mb_substr($highlightedFields['supplierScope'][0], -5, null, 'utf-8');
                if ($startWord != self::$prefix && !StringUtils::startWith($model->supplierScope, $startWord)) {
                    $tempScope = "..." . $highlightedFields['supplierScope'][0];
                } else {
                    $tempScope = $highlightedFields['supplierScope'][0];
                }
                if ($endWord != self::$suffix && !StringUtils::endWith($model->supplierScope, $endWord)) {
                    $tempScope = $tempScope . '...';
                }
                $model->supplierScope = $tempScope;
            }
            if (isset($highlightedFields['supplierLocationSearch']) && is_array($highlightedFields['supplierLocationSearch'])) {
                if (StringUtils::startWith($model->supplierCityName, $model->supplierProvinceName)) {
                    $model->supplierLocationName = $highlightedFields['supplierLocationSearch'][1];
                } else {
                    $model->supplierLocationName = implode("", $highlightedFields['supplierLocationSearch']);
                }
            }
            if (isset($highlightedFields['supplierBusinessName']) && is_array($highlightedFields['supplierBusinessName'])) {
                $model->supplierBusinessName = implode(" ", $highlightedFields['supplierBusinessName']);
            }
            $preg = '/' . self::$prefix . '(.*)<\/em>/';
            $model->matchedProjectNumber = 0;
            if (isset($highlightedFields['supplierProjectCase'])) {
                foreach ($highlightedFields['supplierProjectCase'] as $hlProject) {
                    if (preg_match($preg, $hlProject)) {
                        $model->matchedProjectNumber++;
                        if ($model->matchedProjectNumber == 1) {
                            array_push($model->matchedProjectName, $hlProject);
                        }
                    }
                }
            }

            $model->matchedTopDeveloperNumber = 0;
            $model->topDeveloper = StringUtils::highlightString($model->topDeveloper, $this->keyword);
            if (isset($highlightedFields['topDeveloper'])) {
                foreach ($highlightedFields['topDeveloper'] as $hlDeveloper) {
                    if (preg_match($preg, $hlDeveloper)) {
                        $model->matchedTopDeveloperNumber++;
                        if ($model->matchedTopDeveloperNumber == 1) {
                            array_push($model->matchedTopDeveloper, $hlDeveloper);
                        }
                    }
                }
                foreach ($model->topDeveloper as $k=>$val) {
                    if (preg_match($preg, $val)) {
                        unset($model->topDeveloper[$k]);
                        array_unshift($model->topDeveloper, $val);
                    }
                }
            }

            $model->matchedTopBusinessAreaNumber = 0;
            $model->topBusinessArea = StringUtils::highlightString($model->topBusinessArea, $this->keyword);
            if (isset($highlightedFields['topBusinessArea'])) {
                foreach ($highlightedFields['topBusinessArea'] as $hlBusinessArea) {
                    if (preg_match($preg, $hlBusinessArea)) {
                        $model->matchedTopBusinessAreaNumber++;
                        if ($model->matchedTopBusinessAreaNumber == 1) {
                            array_push($model->matchedTopBusinessArea, $hlBusinessArea);
                        }
                    }
                }
                foreach ($model->topBusinessArea as $k=>$val) {
                    if (preg_match($preg, $val)) {
                        unset($model->topBusinessArea[$k]);
                        array_unshift($model->topBusinessArea, $val);
                    }
                }
            } else {
                $model->supplierLocationName = $model->supplierCityName;
            }

            if ($item['_score'] > $preciseMatchScore && $key === 0) {//只精准匹配一个
                $model->isPreciseMatch = true;
            } else {
                $model->isPreciseMatch = false;
            }
            $model->score = $item['_score'];
            array_push($result['data'], $model);
        }
        $result['mismatch'] = false;
        if ($result['max_score'] < SystemConfigService::get('supplierbigdata_min_score')) {
            $result['mismatch'] = true;//mismatch为true时，显示分词后的关键词推荐
        }
        //添加搜索记录
        if ($this->page == 1 && $this->portal != 'cron') {
            $this->addSearchRecord($this->keyword, $this->keyword, $result['found_number'], "supplierbigdata",
                $this->portal);
        }
        return $result;
    }

    /**
     * 供应商智能提示入口
     * @return array
     */
    public function getSuggester()
    {
        $suggestResult = ['error_code' => 0, 'found_number' => 0, 'data' => []];
        if (empty($this->keyword)) {
            return $suggestResult;
        }
        $data = $this->searchDocuments($this->builderSuggestQueryDSL());
        $result = $data['suggest']['mySuggester'];
        $i = 0;
        foreach ($result as $item) {
            if (count($item['options']) > 0 ) {
                $suggestResult['data'][] = $item['options'][0]['text'];
                $i++;
            }
        }
        $suggestResult['found_number'] = $i;
        return $suggestResult;
    }

    private function builderSuggestQueryDSL()
    {
        //设置高亮
        $query = [
            '_source' => ['supplierName'],
            'size' => 10,
            'from' => 0,
            'suggest' => [
                'mySuggester' => [
                    'prefix' =>  $this->keyword,
                    'completion' => [
                        'field' => 'supplierName.suggest'
                    ]
                ]
            ],
        ];
        if (!empty($this->orderBy)) {
            $query['sort'] = [
                '_score' => [
                    $this->orderBy => $this->sort
                ]
            ];
        }
        return $query;
    }

    /**
     * @return mixed
     */
    private function constructWords()
    {
        $query =[
            '_source' => [],
            'size' => 0,
            'from' => 0,
            'aggs' => [
                'type_group' => [
                    'terms' => ['field' => 'type'],
                ]
            ],
            'query' => [
                'match' => [
                    'word' => [
                        'query' => $this->keyword,
                    ]
                ]
            ]
        ];
        $esCommand = new Command();
        $esCommand->db = \Yii::$app->get('elasticsearch');
        $esCommand->queryParts = $query;
        $esCommand->index = SupplierWordsModel::index();
        $esCommand->type = SupplierWordsModel::type();
        $preferenceStr = md5(json_encode($esCommand->queryParts) . $this->pageSize);
        $data = $esCommand->search(['preference' => $preferenceStr]);
        $result = [];
        if (empty($data['hits']['total'])) {
            return [];
        }
        foreach ($data['aggregations'] as $item) {
            if (count($item['buckets']) > 0) {
                $result[$item['buckets'][0]['key']] =  $item['buckets'][0]['doc_count'];
            }
        }
        return $result;
    }

    private function searchDocuments($query)
    {
        $esCommand = new Command();
        $esCommand->db = \Yii::$app->get('elasticsearch');
        $esCommand->queryParts = $query;
        $esCommand->index = BigDataSupplierModel::index();
        $esCommand->type = BigDataSupplierModel::type();
        $preferenceStr = md5(json_encode($esCommand->queryParts) . $this->pageSize);
        $data = $esCommand->search(['preference' => $preferenceStr]);
        return $data;
    }

    private function getStatisticsQuery(&$aggsQuery)
    {
        $aggsQuery =[
            'max_RegistCapiRmb' => [
                'max' => ['field' => 'registCapiRmb'],
            ],
            'min_RegistCapiRmb' => [
                'min' => ['field' => 'registCapiRmb']
            ],
            'max_SupplierProjectCaseNumber' => [
                'max' => ['field' => 'supplierProjectCaseNumber'],
            ],
            'min_SupplierProjectCaseNumber' => [
                'min' => ['field' => 'supplierProjectCaseNumber']
            ],
            'max_CooperationDevelopersNumber' => [
                'max' => ['field' => 'cooperationDevelopersNumber'],
            ],
            'min_CooperationDevelopersNumber' => [
                'min' => ['field' => 'cooperationDevelopersNumber']
            ],
        ];
    }

    private function builderQueryDSL()
    {
        $functionsQuery = [];
        $filterQuery = [];
        $searchQuery = [];
        $aggsQuery = [];
        $this->constructSearchQuery($searchQuery, $functionsQuery);
        $this->constructFilterQuery($filterQuery);
        $this->getStatisticsQuery($aggsQuery);
        //设置高亮
        $this->page = intval($this->page) > 0 ? intval($this->page) : 1;
        $this->pageSize = intval($this->pageSize) > 0 ? intval($this->pageSize) : 10;
        $from  = ($this->page - 1) * $this->page;
        $query = [
            '_source' => [],
            'size' => $this->pageSize,
            'from' => $from,
            'aggs' => $aggsQuery,
            'query' => [
                'function_score' => [
                    'query' => [
                        'bool' => [
                            'must' => $filterQuery,
                            'should' => $searchQuery,
                            'minimum_should_match' => '90%',//最小词匹配
                        ],
                    ],
                    'functions' => $functionsQuery,
                    'score_mode' => 'sum',
                ]
            ],
            'highlight' => [
                'pre_tags' => [self::$prefix],
                'post_tags' => [self::$suffix],
                "fields"=>[
                    "supplierName" => new \stdClass(),
                    'supplierIntroduce' => new \stdClass(),
                    'supplierScope' => new \stdClass(),
                    'supplierLocationSearch' => new \stdClass(),
                    'supplierBusinessName' => new \stdClass(),
                    'supplierProjectCase' => new \stdClass(),
                    'topDeveloper' => new \stdClass(),
                    'topBusinessArea' => new \stdClass(),
                ]
            ]
        ];
        if (!empty($this->orderBy)) {
            $query['sort'] = [
                '_score' => [
                    $this->orderBy => $this->sort
                ]
            ];
        }
        return $query;
    }

    /**
     * 构建搜索数组和评分数组
     * @param $query
     * @param $functionsQuery
     */
    private function constructSearchQuery(&$query, &$functionsQuery)
    {
        // 搜索项及权重设置
        if (!empty($this->keyword)) {
            if ($this->newWay && isset($this->segmentFacet[1]) && $this->segmentFacet[1] > 0) {  //有命中品牌词
                $query[] = [
                    'match_phrase' => [
                        'supplierName' =>$this->keyword
                    ]
                ];
                $functionsQuery[] = [
                    'filter' => [
                        'range' => [
                            'registCapiRmb' => [
                                'gte' => 1000
                            ]
                        ],
                    ],
                    'weight' => 100
                ];
                $functionsQuery[] = [
                    'filter' => [
                        'range' => [
                            'registCapiRmb' => [
                                'gte' => 100,
                                'lt' => 1000
                            ]
                        ],
                    ],
                    'weight' => 10
                ];
            } elseif ($this->newWay && isset($this->segmentFacet[2]) && $this->segmentFacet[2] > 0) {    //命中产品服务词
                $query[] = [
                    'multi_match' => [
                        'query' => $this->keyword,
                        'fields' => ['supplierBusinessName', 'supplierScope', 'topBusinessArea']
                    ],
                ];
                //大数据案例数
                $functionsQuery[] = [
                    'filter' => [
                        'range' => [
                            'pointCase' => [
                                'gte' => 10
                            ]
                        ],
                    ],
                    'weight' => 4
                ];
                //注册资本
                $functionsQuery[] = [
                    'filter' => [
                        'range' => [
                            'registCapiRmb' => [
                                'gte' => 2000
                            ]
                        ],
                    ],
                    'weight' => 3
                ];
                //大数据合作记录得分
                $functionsQuery[] = [
                    'filter' => [
                        'range' => [
                            'pointCooperative' => [
                                'gte' => 10
                            ]
                        ],
                    ],
                    'weight' => 4
                ];
                //百强
                $functionsQuery[] = [
                    'filter' => [
                        'exists' => [
                            'field' => 'topDeveloper'
                        ],
                    ],
                    'weight' => 1
                ];
                //top级领域
                $functionsQuery[] = [
                    'filter' => [
                        'exists' => [
                            'field' => 'topBusinessArea'
                        ],
                    ],
                    'weight' => 1
                ];
            } elseif ($this->newWay && isset($this->segmentFacet[4]) && $this->segmentFacet[4] > 0) {    //百强词
                $this->isTopDeveloper = true;
            } else {
                $query[] = [
                    'multi_match' => [
                        'query' => $this->keyword,
                        'fields' => [
                            'supplierName',
                            'supplierBusinessName^3',
                            'supplierScope^1.5',
                            'topBusinessArea^15',
                            'topDeveloper^16',
                            'supplierLocationSearch^5',
                        ]
                    ]
                ];
                //$eDisMaxQuery->setPhraseFields("supplierName^5 supplierBusinessName^5 supplierScope topBusinessArea topDeveloper supplierLocationSearch");

                //大数据案例数
                $functionsQuery[] = [
                    'filter' => [
                        'range' => [
                            'pointCase' => [
                                'gte' => 10
                            ]
                        ],
                    ],
                    'weight' => 1
                ];
                //大数据合作记录得分
                $functionsQuery[] = [
                    'filter' => [
                        'range' => [
                            'pointCooperative' => [
                                'gte' => 10
                            ]
                        ],
                    ],
                    'weight' => 1
                ];
                //百强
                $functionsQuery[] = [
                    'filter' => [
                        'exists' => [
                            'field' => 'topDeveloper'
                        ],
                    ],
                    'weight' => 1
                ];
                //top级领域
                $functionsQuery[] = [
                    'filter' => [
                        'exists' => [
                            'field' => 'topBusinessArea'
                        ],
                    ],
                    'weight' => 1
                ];
            }
        } else {
            //大数据案例数
            $functionsQuery[] = [
                'filter' => [
                    'range' => [
                        'pointCase' => [
                            'gte' => 10
                        ]
                    ],
                ],
                'weight' => 1
            ];
            //大数据合作记录得分
            $functionsQuery[] = [
                'filter' => [
                    'range' => [
                        'pointCooperative' => [
                            'gte' => 10
                        ]
                    ],
                ],
                'weight' => 1
            ];
            //百强
            $functionsQuery[] = [
                'filter' => [
                    'exists' => [
                        'field' => 'topDeveloper'
                    ],
                ],
                'weight' => 1
            ];
            //top级领域
            $functionsQuery[] = [
                'filter' => [
                    'exists' => [
                        'field' => 'topBusinessArea'
                    ],
                ],
                'weight' => 1
            ];
        }

    }

    /**
     * 构造filter query数组
     * @param $query
     */
    private function constructFilterQuery(&$query)
    {
        if (!empty($this->isTopDeveloper)) {
            $query[] = [
                'exists' => [
                    'field' => 'topDeveloper'
                ]
            ];
        }
        // 注册资本过滤
        $range = [];
        if (strlen($this->registCapiRmbMin) > 0) {
            $range['gte'] = intval($this->registCapiRmbMin);
        }
        if (strlen($this->registCapiRmbMax) > 0) {
            $range['lte'] = intval($this->registCapiRmbMax);
        }
        if (!empty($range)) {
            $query[] = [
                'range' => [
                    'registCapiRmb' => $range
                ]
            ];
        }
        // 合作关系过滤
        $cooperationRange = [];
        if (strlen($this->cooperationDevelopersNumberMin) > 0) {
            $cooperationRange['gte'] = intval($this->cooperationDevelopersNumberMin);
        }
        if (strlen($this->cooperationDevelopersNumberMax) > 0) {
            $cooperationRange['lte'] = intval($this->cooperationDevelopersNumberMax);
        }
        if (!empty($cooperationRange)) {
            $query[] = [
                'range' => [
                    'cooperationDevelopersNumber' => $cooperationRange
                ]
            ];
        }
        // 项目案例过滤
        $supplierProjectRange = [];
        if (strlen($this->supplierProjectCaseNumberMin) > 0) {
            $supplierProjectRange['gte'] = intval($this->supplierProjectCaseNumberMin);
        }
        if (strlen($this->supplierProjectCaseNumberMax) > 0) {
            $supplierProjectRange['lte'] = intval($this->supplierProjectCaseNumberMax);
        }
        if (!empty($supplierProjectRange)) {
            $query[] = [
                'range' => [
                    'supplierProjectCaseNumber' => $supplierProjectRange
                ]
            ];
        }
        //公司注册地过滤
        if (!empty($this->locationCode)) {
            $registerLocationCode = [];
            $dictionaryService = new DictionaryService();
            foreach (explode(',', $this->locationCode) as $locationCode){
                $registerLocationCode[] = $locationCode;
                $region = $dictionaryService->getRegionHierarchyByCode($locationCode);
                if (!empty($region) && !empty($region->childRegionNodes)) {
                    foreach ($region->childRegionNodes as $childRegion) {
                        $registerLocationCode[] = $childRegion->code;
                    }
                }
            }
            $query[] = [
                'terms' => [
                    'supplierCityCode' => $registerLocationCode
                ]
            ];
        }

        //一级服务分类code查询过滤
        if (!empty($this->businessCode1)) {
            $query[] = [
                'term' => [
                    'supplierBusinessCode1' => $this->businessCode1
                ]
            ];
        }

        //二级服务分类code查询过滤
        if (!empty($this->businessCode2)) {
            $query[] = [
                'term' => [
                    'supplierBusinessCode2' => $this->businessCode2
                ]
            ];
        }

        //三级服务分类code查询过滤
        if (!empty($this->businessCode3)) {
            $codeLevel3 = explode(",", $this->businessCode3);
            $query[] = [
                'terms' => [
                    'supplierBusinessCode2' => $codeLevel3
                ]
            ];
        }

        //服务资质过滤
        if (!empty($this->qualificationCode)) {
            $qualifyCodeList = explode(",", $this->qualificationCode);
            $query[] = [
                'terms' => [
                    'supplierQualificationCode' => $qualifyCodeList
                ]
            ];
        }

        //供应商id
        if (!empty($this->supplierId)) {
            $supplierIdList = explode(",", $this->supplierId);
            $query[] = [
                'terms' => [
                    'supplierId' => $supplierIdList
                ]
            ];
        }
    }

    public function searchDimName()
    {
        $data = [];
        if (!is_numeric($this->page) || $this->page < 1) {
            $this->page = 1;
        } else {
            $this->page = intval($this->page);
        }

        if ($this->keyword) {
            $data = $this->searchDocuments($this->builderNameQueryDSL());
        }
        if (empty($data['hits']['total'])) {
            return [];
        }
        $documents = $data['hits']['hits'];
        $result['error_code'] = 0;
        $result['found_number'] = $data['hits']['total'];
        $result['max_score'] = $data['hits']['max_score'];
        $result['stats'] = [];
        foreach ($documents as $item) {
            $result['data'][] = $item['_source'];
        }
        return $result;
    }

    private function builderNameQueryDSL()
    {
        $from = ($this->page - 1) * $this->pageSize;
        $query = [
            '_source' => [],
            'size' => $this->pageSize,
            'from' => $from,
            'query' => [
                'bool' => [
                    'should' => [
                        'match' => [
                            'supplierName' =>$this->keyword
                        ],
                    ],
                    'minimum_should_match' => '90%',
                ],
            ],
        ];
        return $query;
    }
}